package com.bsj.power.pls.queue.service.impl.handle;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.consumerQ.function.HandleRunT;
import com.bsj.power.common.config.cached.*;
import com.bsj.power.common.config.redis.AlarmConstant;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.power.common.constant.AlarmEnum;
import com.bsj.power.common.constant.MqTopicAndTagConstants;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.def.dto.cardReport.BindCardDTO;
import com.bsj.power.common.def.entity.HighRise;
import com.bsj.power.common.def.entity.alarmReport.AlarmReport;
import com.bsj.power.common.def.entity.job.Job;
import com.bsj.power.common.def.entity.job.WorkPersonnel;
import com.bsj.power.common.def.entity.locationDevice.LocationDevice;
import com.bsj.power.common.def.entity.path.Path;
import com.bsj.power.common.def.entity.path.PathPoint;
import com.bsj.power.common.def.entity.pathReport.PathReport;
import com.bsj.power.common.def.entity.personGroup.BindGroupData;
import com.bsj.power.common.def.entity.personnel.Personnel;
import com.bsj.power.common.def.enums.FileTypeEnum;
import com.bsj.power.common.def.handleData.path.MidPathAlarmState;
import com.bsj.power.common.def.handleData.path.MidPathNoticePerson;
import com.bsj.power.common.def.mq.sdk.SdkAnnex;
import com.bsj.power.common.def.vo.WebSocketMsgVO;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.mapper.*;
import com.bsj.power.common.util.DateUtils;
import com.bsj.power.common.util.ExceptionUtil;
import com.bsj.power.common.util.FindMediaDevUtils;
import com.bsj.power.common.util.StringUtils;
import com.bsj.power.pls.access.PathUseUtil;
import com.bsj.power.pls.config.cached.HighRiseCached;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yinhao
 * @version 1.0
 * @description 进出围栏报警
 * @date 2024/8/19
 */
@Slf4j
@Service
public class QueuePathAlarmHandle implements HandleRunT<HttpTagPosVO> {

    @Value("${annexFile.path}")
    private String annexPath;
    /**
     * true代表核电环境，false代表公司测试环境
     */
    @Value("${systemFlag}")
    private Boolean systemFlag;

    @Value("${myUrl.ipHost}")
    private String ipHost;

    @Autowired
    private PathCached pathCached;

    @Autowired
    private RedisCached redisCached;

    @Autowired
    private BindCardCached bindCardCached;

    @Autowired
    private LocationDeviceCached locationDeviceCached;

    @Autowired
    private PersonnelCached personnelCached;

    @Autowired
    private WorkPersonnelCached workPersonnelCached;

    @Autowired
    private PathAlarmCached pathAlarmCached;

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Resource
    private AlarmReportMapper alarmReportMapper;

    @Resource
    private PathReportMapper pathReportMapper;

    @Resource
    private JobPersonnelMapper jobPersonnelMapper;

    @Resource
    private PersonnelMapper personnelMapper;

    @Resource
    private WorkPersonnelMapper workPersonnelMapper;

    @Override
    public void doRun(List<HttpTagPosVO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        try {
            List<Path> paths = pathCached.hgetAllPathInfo();
            for (HttpTagPosVO httpTagPosVO : list) {
                //位置点信息，Y为高度,先当做平面，只计算X,Z的范围
                double[] pos = httpTagPosVO.getReducePos();
                if (pos == null) {
                    log.warn("未获取到围栏判断位置点数据");
                    continue;
                }
                int mapId = httpTagPosVO.getMapId();
                if (mapId == 0) {
                    log.warn("未获取到定位设备MapId: {}", httpTagPosVO.getSn());
                    continue;
                }
                HighRise high = HighRiseCached.getHigh(mapId);
                if (high == null) {
                    log.warn("未找到系统定义MapId: {}", mapId);
                    continue;
                }
                Integer posSpace = HighRiseCached.getSpace(mapId);
                if (posSpace == 0) {
                    log.warn("设备位置机组未定义: {}", mapId);
                    continue;
                }
                long id = httpTagPosVO.getId();
                //aX, aY, aZ是根据地图三维模型来做判别,存储还是不变
                double aX = pos[0];
                //人员位置跟3D模型的不一致,人员位置的y应换成第三位
                double aY = pos[2];
                //取反值
                double aZ = -pos[1];
                //真实的人员位置
                double x = pos[0];
                double y = pos[1];
                double z = pos[2];
                //定位时间
                long time = httpTagPosVO.getTime();
                Date date = new Date(time);
                //UWB设备号
                String sn = httpTagPosVO.getSn();
                LocationDevice locationDevice = locationDeviceCached.hgetLocationDeviceInfo(sn);
                if (locationDevice == null) {
                    log.info("设备信息不存在: {}", sn);
                    continue;
                }
                //绑卡信息
                BindCardDTO bindCardDTO = bindCardCached.hgetBindCardInfo(RedisConstant.BIND_CARD_REDIS_DEVICE_NAME_KEY + sn);
                if (bindCardDTO == null) {
                    log.info("设备未绑卡不进行进出围栏报警处理{}", sn);
                    continue;
                }
                //负责人员信息
                Personnel personnel = personnelCached.hgetPersonnelInfo(bindCardDTO.getEmpNo());
                if (personnel != null) {
                    //负责人的话不产生报警，但是需要进行弹框通知
//                    log.info("负责人绑卡进行弹框通知判断: {}", sn);
//                    PathUseUtil.noticeFormPerson(paths, pathCached, sn, aX, aZ, redisCached, personnel,
//                            time, pos, baseMqPushUtil, locationDevice, posSpace, mapId);
//                    continue;
                }
                if (personnel == null) {
                    personnel = personnelMapper.selectOne(Wrappers.<Personnel>lambdaQuery()
                            .eq(Personnel::getEmpNo, bindCardDTO.getEmpNo()));
                    personnelCached.hsetPersonnelInfo(personnel);
                }
                //作业人员信息
                WorkPersonnel workPersonnel = workPersonnelCached.getWorkPersonnelInfo(bindCardDTO.getEmpNo());
                if (workPersonnel == null) {
                    workPersonnel = workPersonnelMapper.selectOne(Wrappers.<WorkPersonnel>lambdaQuery()
                            .eq(WorkPersonnel::getTemporaryEmpNo, bindCardDTO.getEmpNo()));
                    //重新写入作业人员信息
                    workPersonnelCached.setWorkPersonnelInfo(workPersonnel);
                }
                if (personnel == null && workPersonnel == null) {
                    log.info("无效的人员信息,工号: {}", bindCardDTO.getEmpNo());
                    continue;
                }
                Integer personnelId = null;
                String personnelName = null;
                Integer personType = null;
                String empNo = null;
                if (workPersonnel != null) {
                    personnelId = workPersonnel.getWorkPersonnelId();
                    personnelName = workPersonnel.getName();
                    personType = NumericalConstants.TWO;
                    empNo = workPersonnel.getTemporaryEmpNo();
                } else {
                    if (personnel != null) {
                        personnelId = personnel.getPersonnelId();
                        personnelName = personnel.getName();
                        personType = NumericalConstants.ONE;
                        empNo = personnel.getEmpNo();
                        //保存负责人在此区域状态
//                        pathCached.setLeadMapId(mapId, personnelId);
                    }
                }
                Map<String, BindGroupData> bindGroupDataMap = pathCached.hgetAllSuperGroup();
                Integer deviceType = locationDevice.getDeviceType();
                String nikeName = locationDevice.getNikeName();
                List<AlarmReport> insertAlarmReport = new ArrayList<>();
                List<PathReport> insertPathReport = new ArrayList<>();
                //获取该区域是否有负责人在场
//                String leadMapId = pathCached.getLeadMapId(mapId);

                for (Path path : paths) {
                    try {
                        //是否禁用
                        Integer isBan = path.getIsBan();
                        if (isBan != null && (isBan).equals(NumericalConstants.ONE)) {
                            continue;
                        }
                        //围栏ID
                        Integer pathId = path.getPathId();
                        //围栏名称
                        String pathName = path.getPathName();
                        // 围栏地址
                        String pathAddress = path.getPathAddress();
                        //围栏空间坐标
                        Integer pathMapId = path.getMapId();
                        //房间号
                        String roomNo = path.getRoomNo();
                        if (StringUtils.isEmpty(pathAddress)) {
                            pathAddress = pathName;
                        }
                        if (pathMapId == null) {
                            //围栏信息错误
                            log.info("围栏空间: {}, 围栏空间坐标ID不对，无效mapId: {}", pathName, pathMapId);
                            continue;
                        }
                        //需要确认空间是否一致
                        //围栏的机组信息
                        Integer pathSpace = HighRiseCached.getSpace(pathMapId);
                        if (!posSpace.equals(pathSpace)) {
                            log.info("围栏空间: {}, 坐标与设备位置点不匹配，设备mapId: {}, 围栏mapId:{}", pathName, mapId, pathMapId);
                            continue;
                        }
                        //围栏类型 1进围栏 2出围栏
                        Integer pathStatus = path.getStatus();
                        //首先判断时间
                        boolean comparison = timeCompare(date, path.getBeginTime(), path.getEndTime());
                        if (!comparison) {
                            log.info("时间不在范围里,围栏信息: {}, 围栏开始结束时间:{} - {},  设备数据时间：{}", pathName, path.getBeginTime(), path.getEndTime(), DateUtils.dateToStr(date));
                            continue;
                        }
                        //关联摄像头信息
                        List<Long> cameraIds = pathCached.smemsPathDevice(pathId);
                        //判断是否在该围栏内
                        List<PathPoint> pathPoints = path.getPathPoints();
                        List<String> xyAddList = pathPoints.stream().map(PathPoint::toAddString).collect(Collectors.toList());
                        String areaCoordinates = xyAddList.stream().collect(Collectors.joining("|"));
                        //1.判断当前人员是否在此区域内
                        boolean inPoly = FindMediaDevUtils.IsPathMapInPoly(aX, aZ, areaCoordinates, mapId, pathMapId);
//                        if (!inPoly) {
//                            log.info("人员位置与围栏位置不匹配: {}-{}, 围栏名称: {}", aX, aZ, pathName);
//                        }
                        //2.判断该围栏是否有关联房间,如果关联房间号则需要找出对应房间是否有关联关键敏感区域的工单数据
                        Map<String, String> roomJobMap = null;
                        if (StringUtils.isNotEmpty(roomNo)) {
                            //获取出该围栏下的所有敏感区域的工单数据
                            roomJobMap = redisCached.hgetAll(RedisConstant.JOB_ROOM_NO_INFO_KEY + roomNo);
                        }
                        //true 需要报警 false 不需要报警，但是需要记录进出记录
                        Boolean alarmFlag = PathUseUtil.authGroupInfo(bindGroupDataMap, empNo,
                                path, personType, roomJobMap, inPoly, bindCardCached, redisCached,
                                areaCoordinates);
                        //根据设备号跟围栏ID获取中间状态
                        String midPathState = redisCached.hget(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "");
                        //围栏报警中间状态
                        MidPathAlarmState midPathAlarmState;
                        if (midPathState == null) {
                            midPathAlarmState = MidPathAlarmState.builder()
                                    .time(time).x(x).y(y).z(z).build();
                        } else {
                            midPathAlarmState = JSON.parseObject(midPathState, MidPathAlarmState.class);
                        }
                        //报警对象
                        // midState=0 即中间状态是正常的(第一次创建中间状态时默认正常) midState=1 中间状态不正常
                        int midState = midPathAlarmState.getState();
                        Integer alarmLevel = pathCached.hgetAlarmLevel(pathStatus);
                        //1 禁止进入围栏，对应进围栏报警
                        if (pathStatus.equals(NumericalConstants.ONE)) {
                            PathUseUtil.notEnterPath(midState, sn, pathId, pathName, pathAddress, pathStatus, inPoly,
                                    personnelId, personnelName, empNo, personType, nikeName, deviceType, time,
                                    pos, redisCached, pathAlarmCached, midPathAlarmState,
                                    cameraIds, insertAlarmReport, baseMqPushUtil, annexPath,
                                    pathPoints, workPersonnel, jobPersonnelMapper, personnelMapper,
                                    alarmLevel, alarmFlag, insertPathReport, systemFlag, ipHost, id);
                        } else if (pathStatus.equals(NumericalConstants.TWO)) {
                            PathUseUtil.notDriveOutPath(midState, sn, pathId, pathName, pathStatus, inPoly,
                                    personnelId, personnelName, empNo, personType, nikeName, deviceType, time,
                                    pos, redisCached, pathAlarmCached, midPathAlarmState,
                                    cameraIds, insertAlarmReport, baseMqPushUtil, annexPath,
                                    workPersonnel, jobPersonnelMapper, personnelMapper,
                                    alarmLevel, alarmFlag, insertPathReport);
                        }
                    } catch (Exception e) {
                        log.info("围栏计算错误: {}, 异常信息: {}", path.toString(), ExceptionUtil.getStackStr(e));
                        continue;
                    }
                }
                try {
                    if (!insertAlarmReport.isEmpty()) {
                        //报表存储
                        insertAlarmReport.stream().forEach(item -> {
                            alarmReportMapper.insert(item);
                        });
                    }
                    if (!insertPathReport.isEmpty()) {
                        //进出围栏通知存储
                        pathReportMapper.bathAddPathReport(insertPathReport);
                    }
                } catch (Exception e) {
                    log.info("进出围栏报警存储错误: {}, 异常信息: {}", ExceptionUtil.getStackStr(e));
                    continue;
                }
            }
        } catch (Exception e) {
            log.error("进出围栏计算错误: {}, 异常信息: {}", ExceptionUtil.getStackStr(e));
            return;
        }
    }


    /**
     * 时间比较
     * 在两个时间范围内返回true，支持跨天
     *
     * @param date
     * @param beginTime
     * @param endTime
     * @return boolean
     * @author ljx
     * @time 2024/5/21 19:23
     */
    private static boolean timeCompare(Date date, Time beginTime, Time endTime) {
        Time time = Time.valueOf(DateUtils.dateToStr(DateUtils.TIME_STANDARD_FORMAT_HMS, date));
        boolean flag1 = time.compareTo(beginTime) >= NumericalConstants.ZERO;
        boolean flag2 = time.compareTo(endTime) <= NumericalConstants.ZERO;
        if (beginTime.compareTo(endTime) >= NumericalConstants.ZERO) {
            //代表跨天,开始时间小于当前时间并且结束时间大于当前时间
            flag1 = !flag1;
        }
        return flag1 && flag2;
    }

    public static void main(String[] args) {

        boolean b = timeCompare(new Date(), Time.valueOf("11:30:30"), Time.valueOf("23:28:30"));
        System.out.println(b);
//        Set<String> s = new HashSet<>();
//        s.add("123");
//        s.add("456");
//        List<String> list = new ArrayList<>(s);
//        List<Long> collect = list.stream().map(Long::parseLong).collect(Collectors.toList());
//        System.out.println(s);
//        System.out.println(collect);
    }
}
